# 8、3 返回值
# 函数并非总是直接显示输出，它还可以处理一些数据，并返回一个或一组值
# 函数返回的值称为返回值。在函数中，可以使用return语句将值返回调用函数的那行代码
# 返回值让你能将程序的大部分繁重工作移到函数中去完成，从而简化主程序
from re import fullmatch
from sys import flags


# # 8.3.1 返回简单的值
#
# def get_formatted_name(first_name, last_name):
#     """返回标准格式的姓名"""
#     full_name = f"{first_name} {last_name}"
#     return full_name.title()
#
# musician = get_formatted_name('jimi', 'hendrix')
# print(musician)
#
# # 8.3.2 让实参变成可选的 —— 使用默认值
# # 有时候，需要让实参变成可选的，以便使用函数的人只在必要时才提供额外的信息
# # 可以使用默认值来让实参变成可选的
#
# # 假设要扩展get_formatted_name() 函数，使其除了名和姓之外还可以处理中间名
# # 可以修改如下:
#
# def get_formatted_name(first_name, middle_name, last_name):
#     """返回标准格式的姓名"""
#     full_name = f"{first_name} {middle_name} {last_name}"
#     return full_name.title()
#
# musician = get_formatted_name('john', 'lee', 'hooker')
# print(musician)
#
# # 对于这个函数，只要同时提供名、中间名和姓，这个函数就能正确运行。在适当的地方加上空格就能转换为首字母大写的格式
# # .title() 方法会自动识别，会让一个字符串中的每个单词的首字母都大写
#
# # 然而，并非所有的人都有中间名，调用这个函数时只提供名和姓，函数将无法正常运行
# # 为了让中间名变成可选的，可以给形参middle_name 指定一个默认值（空字符）
# # 这样即使在用户不提供中间名的时候，函数也能正常运行
#
# def get_formatted_name(first_name, last_name, middle_name=''):  # 注意让设置了默认值的形参放在末尾
#     """返回标准格式的姓名"""
#     if middle_name: # 以middle_name 直接作为条件，当他是空字符时返回 NULL ，不是就条件成立
#         full_name = f"{first_name} {middle_name} {last_name}"
#     else:
#         full_name = f"{first_name} {last_name}"
#     return full_name.title()
#
# musician1 = get_formatted_name('jimi', 'hendrix')
# print(musician1)
#
# musician2 = get_formatted_name('john', 'hooler', 'lee')
# print(musician2)
#
# # 8.3.3 返回字典
# # 函数可返回任何类型的值，包括列表和字典等较为复杂的数据结构
# # 例如，下面的函数接受姓名的组成部分，并返回表示人的字典
#
# def build_person(first_name, last_name):
#     """返回一个字典，其中包含有关一个人的信息"""
#     person = {'first': first_name, 'last': last_name}
#     return person
#
# musician = build_person('jhmi', 'hendrix')
# print(musician)
#
# # 这个函数接受简单的文本信息，并将其放在一个更合适的数据结构中，让你不仅能打印这些信息，还能以其他方式去处理他们
# # 你可以轻松扩大这个函数，以便接受可选值，如中间名，年龄，职业或者其他信息
#
# def build(first_name, last_name, age=None):
#     """返回一个字典，其中包含有关一个人的信息"""
#     person = {'first': first_name, 'last': last_name}
#     if age:
#         person['age'] = age # 如果age的值不是空值，在person 字典中添加键值对
#     return person
#
# musician = build('jimi', 'hendrix', 27)
# print(musician)
#
# # 8.3.4 结合使用函数和while 循环
# # 可将函数与本书前面能介绍的所有Python结构结合起来使用
# # 下面结合使用 get_formatted_name() 函数和 while 循环，以更加正规的方式问候用户
#
# def ger_formatted_name(first_name, last_name):
#     """返回规范格式和姓名"""
#     full_name = f"{first_name} {last_name}"
#     return full_name.title()
#
# flag = 1
# while flag:
#     print("\nPlease tell me your name:")
#     f_name = input("First_name: ")
#     l_name = input("Last_name: ")
#
#     formatted_name = get_formatted_name(f_name, l_name)
#     print(f"\nHello, {formatted_name}!")
#     print("您还需要继续输入吗？")
#     flag = int(input("继续输入请按1，结束请按0"))
#
# # 也可以使用break来跳出循环
#
# while True:
#     print("\nPlease tell me your name:")
#     print("(enter 'quit' at any time to quit)")
#
#     f_name = input("First_name: ")
#     if f_name == 'quit':
#         break
#
#     l_name = input("Last_name: ")
#     if l_name == 'quit':
#         break
#
#     formatted_name = get_formatted_name(f_name, l_name)
#     print(f"\nHello, {formatted_name}!")

# 8、4 传递列表

# 你经常会发现，向函数传递列表很有用，可能是名字列表、数值列表或者更复杂的对象列表（如字典）
# 将列表传递给函数后，函数就能直接访问其中内容

# 假设有一个用户列表，而我们要向其中的每个用户发出问候

def greet_users(names): # 这个函数的形参是一个列表
    """向列表中的每个用户发出简单的问候"""
    for name in names:
        msg = f"Hello, {name.title()}"
        print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)

# 我们将greet_users() 定义成接受一个名字列表，并将其赋给形参names
# 这个函数遍历所收到的列表 ，并对其中的每个用户打印一条问候语。
# 在函数外，先定义一个用户列表 usernames ，在强调用greet_users()并将这个列表传递给它

# 8.4.1 在函数中修改列表
# 将列表传递给函数后，函数就可以对其进行修改了
# 在函数中对这个列表所做的任何修改都是永久的，让你能够高效的处理大量数据

# 现在有一家为用户提交的设计制作3D打印模型的公司
# 需要打印的设计实现存储在一个列表中，打印后将被移到另一个列表中

# 首先创建一个列表，其中包含一些要打印的设计
unprinted_designeds = {'phone case', 'robot pendant', 'dodecahedron'}
completed_models = []

# 模拟打印每个设计，直到们没有未打印的设计为止
# 打印每个设计后，都将其转移到列表 completed_models 中
while unprinted_designeds: #每次都转移一个元素，直到这个列表时空列表时，就会停止循环
    current_design = unprinted_designeds.pop() #按顺序弹出列表中的元素
    print(f"Printing model: {current_design}")
    completed_models.append(current_design)

# 显示打印好的所有模型
print("\nThe following models have been printed: ")
for completed_model in completed_models:
    print(completed_model)

# 可以重新组织这些代码，编写两个函数，让每个都做一件具体的工作。
# 大部分代码与原来的相同，之时结构更加合理
# 第一个函数负责处理打印设计的工作，第二个函数打印了那些设计：

def print_models(unprinted_designs, completed_models):
    """模拟打印每个设计，直到没有未打印的设计为止，打印每个设计后，
    都将其转移到列表 completed_models 中"""
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print(f"Printing model: {current_design}")
        completed_models.append(current_design)

def show_completed_models(completed_models):
    """显示打印好的所有模型"""
    print("\nThe following models have been printed: ")
    for completed_model in completed_models:
        print(f"Printing model: {completed_model}")

unprinted_designeds = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designeds, completed_models)
show_completed_models(completed_models)

# 首先，定义函数 print_models() 它包含两个形参：一个需要打印的设计列表和一个打印好的模型列表
# 给定这两个列表，这个函数模拟打印每个设计的过程：将设计逐个从未打印的设计列表中取出，并加入打印好的模型列表，
# 然后定义 show_completed_models() ，它包含一个形参：打印好的模型列表。

# 我们创建了一个未打印的设计列表，以及一个空列表，后者用于存储打印好的模型
# 接下来，由于已经定义了两个函数，依次只需要调用他们并传入正确的实参即可
# 我们调用print_models() 并向它传递这两个列表。

# 这个程序还演示了以一种理念：每个函数都只应负责一项具体工作。
# 用第一个函数打印每个设计，用第二个函数显示打印好的模型，由于使用一个函数完成这两项工作
# 在别写函数时，如果发现她执行的任务太多，请尝试将这些代码划分到两个函数中
# 别忘了，总是可以在一个函数中调用另一个函数，这有助于将复杂的任务分解成一系列的步骤

# 8.4.2 禁止函数修改列表 —— 切片表示法创建列表的副本

# 有时候，需要禁止函数修改列表。
# 假如像前一个示例那样，你有一个未打印的设计列表，并且编写了一个将这些设计移到打印好的模型列表中的函数
# 你可能会做出这样的决定：即便打印了所有的设计，也要保留原来的未打印的设计列表，作为存档
# 但是由于你将所有的设计都移除了 unprinted_designs,这个列表变成了空的
# 为了解决这个问题，可向函数传递列表的副本而不是原始列表。
# 这样，函数所做的任何修改都只影响副本，而且喜好不影响原始列表
# 要将列表的副本传递给函数，可以像下面这样做：

# function_name(list_name[:])
# 切片表示法 [:] 创建列表的副本。
# 在printing——models 中，如果不想清空未打印的设计列表，可行下面这样调用print_models()

print_models(unprinted_designeds[:], completed_models)
# print_models() 函数依然能够完成其工作，因为他获得了所有未打印的设计的名称，但他这次使用的列表 unprinted——designs 的副本，而不是列表unprinted——designs 本身

# 像以前一样，列表completed_models 将包含打印好的模型的名称，但函数所做的修改不会影响列表 unprinted_designs

# 虽然向函数传递列表的副本可保留原始列表的内容，但除非有充分的理由，否则还是应该将原始列表传递给函数。
# 这是因为，让函数使用现成的列表可避免花时间和内存创建副本，从而提高效率，在处理大型列表时尤其如此


# 8、5 传递任意数量的实参 —————— *形参
# 有时候，你预先不知道函数需要接受多少个实参，好在Python 允许函数从调用语句中收集任意数量的实参

# 例如一个制作比萨的函数，他需要接受很多配料，但无法预先确定顾客要点多少种配料
# 下面的函数只有一个形参 *toppings , 不管调用语句提供了多少实参，这个形参都会将其收入囊中

def make_pizza(*toppings):
    """打印顾客点的所有配料"""
    print(toppings)

make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

# 形参名 *toppings 中的星号 * 让Python 创建一个名为 toppings 的元组
# 该元组包含函数收到的所有值，函数体内的函数调用print() 生成的输出证明
# Python既能处理使用一个值调用函数的情形，也能处理使用三个值调用函数的情形
# 它以类似等方式处理不同的调用。
# 注意，Python 会将实参封装到一个元组中，即便函数只收到一个值也是如此（都是元组，用小括号装起来的，而非中括号）

#  现在可以将函数调用 print 替换为一个循环，遍历配料表并对顾客点的比萨进行描述：

def make_pizza(*toppings):
    """概述要制作的比萨"""
    print("\nMaking a pizza with the following toppings: ")
    for topping in toppings:
        print(f"- {topping}")

make_pizza('pepperoni')
make_pizza('mushurooms', 'green peppers', 'extra cheese')

# 不管收到一个值还是三个值，函数都能妥善处理
# 8.5.1 结合使用位置实参和任意数量的实参
# 如果要让函数接受不同类型的实参，必须在函数定义中将接纳任意数量输惨的形参放在最后
# Python 将先匹配位置实参和关键字实参，在即将余下的实参都收集到最后一个形参中

def make_pizza(size, *toppings):
    """概述将要制作的比萨"""
    print(f"\nMaking a {size}-inch pizza with following toppings: ")
    for topping in toppings:
        print(f"- {topping}")

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

# 基于上述函数定义，Python 将收到的第一个值赋给形参size，将其他所有的值都存储在元素 toppings 中
# 在函数调用中，首先指定表示比萨尺寸的实参，再根据需要指定任意数量的配料

# 经常会看到通用形参名 *args， 它也这样手机任意数量的位置实参

# 8.5.2 使用任意数量的关键字实参
# 有时候你需要指定任意数量的实参，但预先不知道传递给函数的会是什么样的信息
# 在这种情况下，可将函数编写成能够任意熟练的键值对———— 调用语句提供了多少就接受多少

# 一个这样的示例是创建用户简介：你知道将收到有关用户的信息，但不确定是什么样的信息。

def build_profile(first, last, **user_info):
    """此函数不仅接受名和姓，还接受任意数量的关键字实参"""
    # 创建一个字典，其中包含我们知道的有关用户的一切
    # 形参**user_info中的两个星号让Python创建一个名为user_info 的字典
    # 该字典包含函数收到的其他所有名值对
    user_info['first_name'] = first
    user_info['last_name'] = last
    return user_info

user_profile = build_profile('albert', 'einstein',
                             location='princeton',
                             field='physics')
print(user_profile)

# bulid_profile() 函数的定义要求提供和名和姓，同时允许根据需要提供任意熟练的名值对
# 形参 **user_info 中的两个星号让Python 创建一个名为user_info的字典
# 该字典包含函数收到的其他所有名值对。在这个函数中，可以像访问其他字典那样访问user_info 中的名值对

# 在编写函数时，可以用各种方式混合使用位置实参、关键字实参和任意数量的实参

# 你经常会看到形参名 **kwargs， 它用于收集任意数量的关键字实参



































































































